/*
 * Copyright (c) 2006-2020, SZ-ZZS Development Team
 *
 *                            _ooOoo_
 *                           o8888888o
 *                           88" . "88
 *                           (| -_- |)
 *                           O\  =  /O
 *                        ____/`---'\____
 *                      .'  \\|     |//  `.
 *                     /  \\|||  :  |||//  \
 *                    /  _||||| -:- |||||-  \
 *                    |   | \\\  -  /// |   |
 *                    | \_|  ''\---/''  |   |
 *                    \  .-\__  `-`  ___/-. /
 *                  ___`. .'  /--.--\  `. . __
 *               ."" '<  `.___\_<|>_/___.'  >'"".
 *              | | :  `- \`.;`\ _ /`;.`/ - ` : | |
 *              \  \ `-.   \_ __\ /__ _/   .-` /  /
 *         ======`-.____`-.___\_____/___.-`____.-'======
 *                            `=---='
 *        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 *                      Buddha Bless, No Bug !
 * Change Logs:
 * Date           Author       Notes
 * 2020-06-08     Urey       the first version
 */

/*
*********************************************************************************************************
*                                            INCLUDE FILES
*********************************************************************************************************
*/

#define   MB_OS_MODULE
#include <mb.h>
#include <rtthread.h>

#define DBG_TAG             "uMB-OS"
#define DBG_LEVEL           DBG_INFO
#include <rtdbg.h>


/*
*********************************************************************************************************
*                                       LOCAL GLOBAL VARIABLES
*********************************************************************************************************
*/

#if (MODBUS_CFG_MASTER_EN == DEF_ENABLED)
static  struct rt_semaphore MB_OS_RxSemTbl[MODBUS_CFG_MAX_CH];
#endif

#if (MODBUS_CFG_SLAVE_EN  == DEF_ENABLED)
static  struct rt_thread    MB_OS_RxTaskTCB;
static  struct rt_mailbox   MB_OS_RxMb;
static  rt_uint32_t         MB_OS_RxMb_Pool[MODBUS_CFG_MAX_CH + 1];
static  rt_uint8_t          MB_OS_RxTaskStk[MB_OS_CFG_RX_TASK_STK_SIZE];
#endif


/*
*********************************************************************************************************
*                                      LOCAL FUNCTION PROTOTYPES
*********************************************************************************************************
*/

#if (MODBUS_CFG_MASTER_EN == DEF_ENABLED)
static  void  MB_OS_InitMaster(void);
static  void  MB_OS_ExitMaster(void);
#endif

#if (MODBUS_CFG_SLAVE_EN == DEF_ENABLED)
static  void  MB_OS_InitSlave (void);
static  void  MB_OS_ExitSlave (void);
static  void  MB_OS_RxTask    (void  *p_arg);
#endif

/*
*********************************************************************************************************
*                                     LOCAL CONFIGURATION ERRORS
*********************************************************************************************************
*/


/*
*********************************************************************************************************
*                                              MB_OS_Init()
*
* Description : This function initializes the RTOS interface.  This function creates the following:
*
*               (1) A message queue to signal the reception of a packet.
*
*               (2) A task that waits for packets to be received.
*
* Argument(s) : none
*
* Return(s)   : none.
*
* Caller(s)   : MB_Init().
*
* Note(s)     : none.
*********************************************************************************************************
*/

void  MB_OS_Init (void)
{
#if (MODBUS_CFG_MASTER_EN == DEF_ENABLED)
    MB_OS_InitMaster();
#endif

#if (MODBUS_CFG_SLAVE_EN == DEF_ENABLED)
    MB_OS_InitSlave();
#endif
}



/*
*********************************************************************************************************
*                                          MB_OS_InitMaster()
*
* Description : This function initializes and creates the kernel objectes needed for Modbus Master
*
* Argument(s) : none.
*
* Return(s)   : none.
*
* Caller(s)   : MB_OS_Init().
*
* Note(s)     : none.
*********************************************************************************************************
*/

#if (MODBUS_CFG_MASTER_EN == DEF_ENABLED)
static void MB_OS_InitMaster(void)
{
    int i;
    char name[128];

    for (i = 0; i < MODBUS_CFG_MAX_CH; i++)
    {
        rt_memset(name, 0, sizeof(name));
        rt_snprintf(name, sizeof(name), "%s-%d","mbmsem-%d",i);
        rt_sem_init(&MB_OS_RxSemTbl[i], name, 0, RT_IPC_FLAG_FIFO);
    }
}
#endif

/*
*********************************************************************************************************
*                                          MB_OS_InitSlave()
*
* Description : This function initializes and creates the kernel objectes needed for Modbus Salve
*
* Argument(s) : none.
*
* Return(s)   : none.
*
* Caller(s)   : MB_OS_Init().
*
* Note(s)     : none.
*********************************************************************************************************
*/

#if (MODBUS_CFG_SLAVE_EN == DEF_ENABLED)
static  void  MB_OS_InitSlave (void)
{
    rt_err_t  err;

    err = rt_thread_init(&MB_OS_RxTaskTCB,
                         "uModb-RX",
                         MB_OS_RxTask,
                         RT_NULL,
                         &MB_OS_RxTaskStk[0],
                         MB_OS_CFG_RX_TASK_STK_SIZE,
                         MB_OS_CFG_RX_TASK_PRIO,
                         20);

    if(err != RT_EOK)
    {
        LOG_E("Create RxTask ERROR");
        return;
    }

    err = rt_mb_init(&MB_OS_RxMb,
                     "uModb-RMB",
                     &MB_OS_RxMb_Pool[0],
                     MODBUS_CFG_MAX_CH,
                     RT_IPC_FLAG_FIFO);
    if(err != RT_EOK)
    {
        LOG_E("Create RxMb ERROR");
        return;
    }

    rt_thread_startup(&MB_OS_RxTaskTCB);

}
#endif



/*
*********************************************************************************************************
*                                             MB_OS_Exit()
*
* Description : This function is called to terminate the RTOS interface for Modbus channels.  We delete
*               the following uC/OS-II objects:
*
*               (1) An event flag group to signal the reception of a packet.
*               (2) A task that waits for packets to be received.
*
* Argument(s) : none.
*
* Return(s)   : none.
*
* Caller(s)   : MB_Exit().
*
* Note(s)     : none.
*********************************************************************************************************
*/

void  MB_OS_Exit (void)
{
#if (MODBUS_CFG_MASTER_EN == DEF_ENABLED)
    MB_OS_ExitMaster();
#endif

#if (MODBUS_CFG_SLAVE_EN == DEF_ENABLED)
    MB_OS_ExitSlave();
#endif
}

/*
*********************************************************************************************************
*                                          MB_OS_ExitMaster()
*
* Description : This function is called to terminate the RTOS interface for Modbus Master channels.  The
*               following objects are deleted.
*
*               (1) An event flag group to signal the reception of a packet.
*
* Argument(s) : none.
*
* Return(s)   : none.
*
* Caller(s)   : MB_OS_Exit().
*
* Note(s)     : none.
*********************************************************************************************************
*/

#if (MODBUS_CFG_MASTER_EN == DEF_ENABLED)
static void MB_OS_ExitMaster(void)
{
    int i;

    for (i = 0; i < MODBUS_CFG_MAX_CH; i++)
    { /* Delete semaphore for each channel     */
        rt_sem_detach(&MB_OS_RxSemTbl[i]);
    }
}
#endif


/*
*********************************************************************************************************
*                                          MB_OS_ExitSlave()
*
* Description : This function is called to terminate the RTOS interface for Modbus Salve channels.
*               The following objects are deleted.
*
*               (1) A task that waits for packets to be received.
*               (2) A message queue to signal the reception of a packet.
*
* Argument(s) : none
*
* Return(s)   : none.
*
* Caller(s)   : MB_OS_Exit().
*
* Note(s)     : none.
*********************************************************************************************************
*/

#if (MODBUS_CFG_SLAVE_EN == DEF_ENABLED)
void  MB_OS_ExitSlave (void)
{
    rt_err_t  err;

    err = rt_thread_detach(&MB_OS_RxTaskTCB);             /* Delete Modbus Rx Task                 */
    (void)err;

    LOG_D("Detach Modbus Rx Task");
}
#endif




/*
*********************************************************************************************************
*                                              MB_OS_RxSignal()
*
* Description : This function signals the reception of a packet either from the Rx ISR(s) or the RTU timeout
*               timer(s) to indicate that a received packet needs to be processed.
*
* Argument(s) : pch     specifies the Modbus channel data structure in which a packet was received.
*
* Return(s)   : none.
*
* Caller(s)   : MB_ASCII_RxByte(),
*               MB_RTU_TmrUpdate().
*
* Note(s)     : none.
*********************************************************************************************************
*/

void MB_OS_RxSignal(MODBUS_CH *pch)
{
    if (pch != (MODBUS_CH*) 0)
    {
        switch (pch->MasterSlave)
        {
#if (MODBUS_CFG_MASTER_EN == DEF_ENABLED)
            case MODBUS_MASTER:
                rt_sem_release(&MB_OS_RxSemTbl[pch->Ch]);
             break;
#endif

#if (MODBUS_CFG_SLAVE_EN == DEF_ENABLED)
            case MODBUS_SLAVE:
            default:
                rt_mb_send(&MB_OS_RxMb,(rt_uint32_t)pch);
            break;
#endif
        }
    }
}


/*
*********************************************************************************************************
*                                              MB_OS_RxWait()
*
* Description : This function waits for a response from a slave.
*
* Argument(s) : pch     specifies the Modbus channel data structure to wait on.
*
*               perr    is a pointer to a variable that will receive an error code.  Possible errors are:
*
*                       MODBUS_ERR_NONE        the call was successful and a packet was received
*                       MODBUS_ERR_TIMED_OUT   a packet was not received within the specified timeout
*                       MODBUS_ERR_NOT_MASTER  the channel is not a Master
*                       MODBUS_ERR_INVALID     an invalid error was detected
*
* Return(s)   : none.
*
* Caller(s)   : MBM_FCxx()  Modbus Master Functions
*
* Return(s)   : none.
*********************************************************************************************************
*/

void  MB_OS_RxWait (MODBUS_CH   *pch, CPU_INT16U  *perr)
{
#if (MODBUS_CFG_MASTER_EN == DEF_ENABLED)
    rt_err_t  err;

    if (pch != (MODBUS_CH *)0) {
        if (pch->MasterSlave == MODBUS_MASTER) {
            err = rt_sem_take(&MB_OS_RxSemTbl[pch->Ch], pch->RxTimeout);
            switch (err) {
                default:
                     *perr = MODBUS_ERR_INVALID;
                     break;

                case (-RT_ETIMEOUT):
                     *perr = MODBUS_ERR_TIMED_OUT;
                     break;

                case RT_EOK:
                     *perr = MODBUS_ERR_NONE;
                     break;
            }
        } else {
            *perr = MODBUS_ERR_NOT_MASTER;
        }
    } else {
        *perr = MODBUS_ERR_NULLPTR;
    }
#else
    *perr = MODBUS_ERR_INVALID;
#endif
}

/*
*********************************************************************************************************
*                                            MB_OS_RxTask()
*
* Description : This task is created by MB_OS_Init() and waits for signals from either the Rx ISR(s) or
*               the RTU timeout timer(s) to indicate that a packet needs to be processed.
*
* Argument(s) : p_arg       is a pointer to an optional argument that is passed by uC/OS-II to the task.
*                           This argument is not used.
*
* Return(s)   : none.
*
* Caller(s)   : This is a Task.
*
* Return(s)   : none.
*********************************************************************************************************
*/

#if (MODBUS_CFG_SLAVE_EN == DEF_ENABLED)
static  void  MB_OS_RxTask (void *p_arg)
{
    rt_err_t   err;
    rt_ubase_t recv;

    (void)p_arg;

    while (1)
    {
        err = rt_mb_recv(&MB_OS_RxMb, &recv, RT_WAITING_FOREVER);
        if (err == RT_EOK)
        {
            MB_RxTask((MODBUS_CH*) recv);
        }
    }
}
#endif
#if (MODBUS_CFG_RTU_EN == DEF_ENABLED) || (MODBUS_CFG_ASCII_EN == DEF_ENABLED)
RT_WEAK
void         MB_CommExit                (void)
{

}
#endif



#if (MODBUS_CFG_RTU_EN == DEF_ENABLED)
static struct rt_timer _mb_rtu_timer = {};

static void _mb_rtu_timer_timeout_function(void* input)
{
    MB_RTU_TmrCtr++; // Indicate that we had activities on this interrupt
    MB_RTU_TmrUpdate(); // Check for RTU timers that have expired
}

/* Initialize the timer used for RTU framing                    */
void MB_RTU_TmrInit(void)
{
    rt_timer_init(&_mb_rtu_timer,
                  "modbus rtu timer",
                  _mb_rtu_timer_timeout_function,
                  RT_NULL,
                  rt_tick_from_millisecond(1000 / MODBUS_CFG_RTU_FREQ),
                  RT_TIMER_FLAG_SOFT_TIMER | RT_TIMER_FLAG_PERIODIC);

    rt_timer_start(&_mb_rtu_timer);

}

void MB_RTU_TmrExit(void)
{
    rt_timer_detach(&_mb_rtu_timer);
}

#endif

